package middleware

import (
	"com.cyt.common/library/breaker"
	"com.cyt.common/library/limit"
	"com.cyt.common/library/response"
	"com.cyt.common/middleware/casbin"
	"com.cyt.common/middleware/collect"
	"com.cyt.common/middleware/jaeger"
	"com.cyt.common/middleware/jwtauth"
	"com.cyt.common/middleware/limiter"
	"context"
	"fmt"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/ghttp"
	"github.com/gogf/gf/os/glog"
	"github.com/pyroscope-io/pyroscope/pkg/agent/profiler"
	"com.cyt.common/constant"
	"net/http"
	"strings"
	"time"
)

// 中间件管理服务
var Middleware = new(middlewareService)

type middlewareService struct{}

// 允许接口跨域请求
func (s *middlewareService) CORS(r *ghttp.Request) {
	r.Response.CORSDefault()
	r.Middleware.Next()
}

// authHook is the HOOK function implements JWT logistics.
func (s *middlewareService) JwtAuth(r *ghttp.Request) {
	jwtauth.JwtAuth.MiddlewareFunc()(r)
	r.Middleware.Next()
}

// 自定义上下文对象
func (s *middlewareService) Ctx(r *ghttp.Request) {
	// 初始化，务必最开始执行
	r.Middleware.Next()
}

// 防御CSRF攻击
func (s *middlewareService) CSRF(r *ghttp.Request) {
	// 官方的不好用
	r.Middleware.Next()
}

func (s *middlewareService) Limiter(r *ghttp.Request) {
	limiter.New(r)
	r.Middleware.Next()
}

// 自适应熔断
func (s *middlewareService) Breaker() func(r *ghttp.Request) {
	brkMap := g.Map{}

	return func(r *ghttp.Request) {
		brkStr := strings.Join([]string{r.Method, r.URL.Path}, "://")
		if brkMap[brkStr] == nil {
			brkMap[brkStr] = breaker.NewBreaker(breaker.WithName(strings.Join([]string{r.Method, r.URL.Path}, "://")))
		}
		brk := brkMap[brkStr].(breaker.Breaker)
		promise, err := brk.Allow()
		if err != nil {
			glog.Errorf("[http] dropped, %s - %s - %s",
				r.RequestURI, r.Header.Get("X-Forwarded-For"), r.UserAgent())
			r.Response.WriteStatusExit(http.StatusServiceUnavailable)
			return
		}

		defer func() {
			if r.Response.Status < http.StatusInternalServerError {
				promise.Accept()
			} else {
				promise.Reject(fmt.Sprintf("%d %s", r.Response.Status, http.StatusText(r.Response.Status)))
			}
		}()
		r.Middleware.Next()
		return
	}
}

// 最大连接数
func (s *middlewareService) MaxConns(n int) func(r *ghttp.Request) {
	latch := limit.NewLimit(n)

	return func(r *ghttp.Request) {
		if n <= 0 {
			r.Middleware.Next()
		}

		if latch.TryBorrow() {
			defer func() {
				if err := latch.Return(); err != nil {
					glog.Error(err)
				}
			}()

			r.Middleware.Next()
			return
		} else {
			r.Response.WriteStatusExit(http.StatusTooManyRequests)
		}
	}
}

// 最大请求字节数
func (s *middlewareService) MaxBytes(n int64) func(r *ghttp.Request) {
	return func(r *ghttp.Request) {
		if n <= 0 {
			r.Middleware.Next()
		}

		if r.ContentLength > n {
			r.Response.WriteStatusExit(http.StatusRequestEntityTooLarge)
		} else {
			r.Middleware.Next()
			return
		}
	}
}

// 超时处理
func (s *middlewareService) Timeout(t time.Duration) func(r *ghttp.Request) {
	return func(r *ghttp.Request) {
		ctx, cancel := context.WithTimeout(r.Request.Context(), time.Duration(time.Second * t))
		defer cancel()

		select {
		case <-time.After(t * time.Second):
			r.Response.WriteStatusExit(http.StatusGatewayTimeout, "")
		case <-ctx.Done():
		}

		r.Request = r.Request.WithContext(ctx)
		r.Middleware.Next()
		return
	}
}

// 性能剖析
func (s *middlewareService) LoadPProf(r *ghttp.Request) {
	env := g.Cfg().GetString("server.appEnv")
	address := g.Cfg().GetString("pprof.address")
	if env == "local" && address != "" {
		_, _ = profiler.Start(profiler.Config{
			ApplicationName: constant.APP_NAME,
			ServerAddress: address,
		})
	}
	r.Middleware.Next()
}

// 链路追踪
func (s *middlewareService) JaegerTracer() func(r *ghttp.Request) {
	return jaeger.GfTracer()
}

// 采集信息
func (s *middlewareService) CollectInfo() func(r *ghttp.Request) {
	return collect.CollectInfo()
}

// Casbin
func (s *middlewareService) Casbin() func(r *ghttp.Request) {
	return casbin.Casbin()
}

// 处理异常捕获
func (s *middlewareService) ErrorHandler(r *ghttp.Request) {
	r.Middleware.Next()
	if err := r.GetError(); err != nil {
		g.Log("exception").Error(err) // 记录到自定义错误日志文件
		r.Response.ClearBuffer()      //返回固定的友好信息
		response.Result(r, 500, "服务器居然开小差了，请稍后再试吧！")
	}
}
